package com.vhall.component.service.inav.impl;

//--------------------Optional ---------------------------
//region-plugin-ctrl-start

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Splitter;
import com.vhall.component.entity.guosen.dto.GetTghInfoDTO;
import com.vhall.component.entity.guosen.dto.GuoxinFollowTghDTO;
import com.vhall.component.entity.guosen.enums.FollowEnum;
import com.vhall.component.entity.inav.SendMsgPassBodyDTO;
import com.vhall.component.entity.inav.enums.UserGuideShowStatusEnum;
import com.vhall.component.entity.inav.enums.UserGuideShowTypeEnum;
import com.vhall.component.entity.inav.vo.*;
import com.vhall.component.entity.message.MicBroadcastMessageDTO;
import com.vhall.component.entity.room.dto.RoomJoinsEntityDTO;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.entity.room.entity.RoomJoinsEntity;
import com.vhall.component.entity.room.enums.RoomStatusEnum;
import com.vhall.component.entity.room.vo.ReportGuideShownRequestVO;
import com.vhall.component.entity.room.vo.QueryRoomJoinsBySelectOneReqVO;
import com.vhall.component.entity.thirduser.ThirdUserVO;
import com.vhall.component.entity.thirduser.vo.GuoxinInvestmentAdvisorTeamDTO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.constant.RoomConstant;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.support.Token;
import com.vhall.component.plugin.common.support.TokenThreadLocal;
import com.vhall.component.plugin.common.support.VssToken;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.core.service.GuoXinService;
import com.vhall.component.service.inav.InavService;
import com.vhall.component.service.inav.WheatCacheService;
import com.vhall.component.service.inav.constant.InavConstant;
import com.vhall.component.service.livecolumn.LiveColumnService;
import com.vhall.component.service.praise.RoomLikeService;
import com.vhall.component.service.room.RoomJoinService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author lsl
 * @date 2021/6/7
 */
@Slf4j
@Service
public class InavServiceImpl extends BaseService implements InavService {
    @Value("${spring.profiles.active:''}")
    private String active;

    @Autowired
    private RoomLikeService roomLikeService;

    @Autowired
    private WheatCacheService wheatCacheService;
    @Autowired
    private LiveColumnService liveColumnService;
    @Autowired
    private GuoXinService guoXinService;
    @Autowired
    private RoomJoinService roomJoinService;

    @Override
    public Boolean setDevice(SetDeviceReqVO param) {
        String roomId = param.getRoomId();
        String accountId = param.getAccountId();
        RoomJoinsEntityDTO roomJoinInfo = roomInnerService.getRoomJoins(Integer.valueOf(accountId), roomId);
        if (roomJoinInfo == null) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        //更新(设备状态)并进行缓存
        roomInnerService.updateRoomJoins(roomJoinInfo, param.getType(), param.getStatus());
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(param.getRoomId());

        //组装PaaS参数
        JSONObject json = new JSONObject();
        json.put("type", RoomConstant.VRTC_DEVICE_CHECK);
        // 参会id
        json.put(InavConstant.ROOM_JOIN_ID, roomJoinInfo.getAccountId());
        // 用户角色（1:老师 2:学员 3:助教 4:嘉宾 5:监课）
        json.put(InavConstant.ROOM_ROLE, roomJoinInfo.getRoleName());
        // 昵称
        json.put(InavConstant.NICK_NAME, roomJoinInfo.getNickname());
        // 设备类型0其他1手机端2PC
        json.put(InavConstant.DEVICE_TYPE, roomJoinInfo.getDeviceType());
        // 设备状态0未检测1可上麦2不可上麦
        json.put(InavConstant.DEVICE_STATUS, roomJoinInfo.getDeviceStatus());
        paasService.sendMessage(roomInfo.getChannelId(), json.toJSONString());
        return true;
    }

    @Override
    public Boolean setHandSup(SetHandSupReqVO vo, VssToken token) {
        String roomId = vo.getRoomId();
        String status = vo.getStatus();
        RoomsEntityDTO room = roomInnerService.getRoomInfo(roomId);
        if (room == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        if (RoomConstant.STATUS_START != room.getStatus()) {
            throw new BusinessException(BizErrorCode.BIZ_NOT_START);
        }
        Integer accountId = token.getThirdPartyUserId();
        RoomJoinsEntityDTO roomJoinInfo = roomInnerService.getRoomJoins(accountId, roomId);
        if (roomJoinInfo == null) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        if (!RoomConstant.HOST.equals(roomJoinInfo.getRoleName())) {
            //无此操作权限  30008
            throw new BusinessException(BizErrorCode.BIZ_NO_PERMISSION);
        }
        redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + roomId, RoomConstant.IS_HANDSUP, status);
        //PaaS参数组装
        JSONObject json = new JSONObject();
        if (RoomConstant.UP.equals(status)) {
            json.put("type", RoomConstant.VRTC_UP);
        } else {
            json.put("type", RoomConstant.VRTC_DOWN);
        }
        paasService.sendMessage(room.getChannelId(), json.toJSONString());
        return true;
    }

    @Override
    public void setDesktop(SetDesktopReqVO vo, VssToken token) {
        String roomId = vo.getRoomId();
        Integer status = vo.getStatus();

        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(roomId);
        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }

        if (status != 0) {
            if (roomInfo.getStatus() != 1) {
                throw new BusinessException(BizErrorCode.BIZ_NOT_START);
            }
        }

        redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + roomId, RoomConstant.IS_DESKTOP, status.toString());
        SendMsgPassBodyDTO passBodyDTO = new SendMsgPassBodyDTO();

        if (status != 0) {
            passBodyDTO.setType("desktop_open");
        } else {
            passBodyDTO.setType("desktop_close");
        }
        String body = JsonUtil.toJsonString(passBodyDTO);
        paasService.sendMessage(roomInfo.getChannelId(), body, null, null, null, null);
    }

    @Override
    public UserStatusRspVO getUserStatus(GetUserStatusReqVO reqVO) {
        RoomJoinsEntity roomJoinsEntity = roomJoinService.getRoomJoin(reqVO.getRoomId(), Integer.valueOf(reqVO.getAccountId()));
//        RoomJoinsEntityDTO roomJoinInfo = roomInnerService.getRoomJoins(Integer.valueOf(reqVO.getAccountId()), reqVO.getRoomId());
        if (roomJoinsEntity == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        UserStatusRspVO vo = new UserStatusRspVO();
        //判断是否禁言
        if (roomJoinsEntity.getIsBanned() != null) {
            vo.setIsBanned(roomJoinsEntity.getIsBanned());
        } else {
            vo.setIsBanned(RoomConstant.DEFAULT_ZERO);
        }
        //判断是否踢出
        if (roomJoinsEntity.getIsKicked() != null) {
            vo.setIsKicked(roomJoinsEntity.getIsKicked());
        } else {
            vo.setIsKicked(RoomConstant.DEFAULT_ZERO);
        }
        Long isLike;
        //region plugin-praise-start
        // 点赞插件
        isLike = roomLikeService.countIsLike(reqVO.getAccountId(), reqVO.getRoomId());
        //endregion-plugin-praise-start

        vo.setIsLike(isLike.intValue());
        return vo;
    }

    @Override
    public void setAllBanned(SetAllBannedReqVO vo, VssToken token) {
        RoomJoinsEntityDTO joinUser = getCurrentJoinUser(vo.getRoomId(), token.getThirdPartyUserId());
        SendMsgPassBodyDTO passBodyDTO = new SendMsgPassBodyDTO();
        redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + joinUser.getRoomId(), RoomConstant.ALL_BANNED, vo.getType());
        RoomsEntityDTO room = roomInnerService.getRoomInfo(joinUser.getRoomId());
        String body;
        if (vo.getType() != 0) {
            passBodyDTO.setType("disable_all");
        } else {
            passBodyDTO.setType("permit_all");
        }
        body = JsonUtil.toJsonString(passBodyDTO);
        paasService.sendCustomMessage(room.getChannelId(), joinUser.getAccountId(), body, "service_im");
    }

    @Override
    public Integer setBanned(SetBannedReqVO vo, VssToken token) {
        List<String> accountIdList = Splitter.on(",").splitToList(vo.getReceiveAccountId());
        Integer thirdId = token.getThirdPartyUserId();
        List<Integer> accountIds = accountIdList.stream().map(Integer::parseInt).collect(Collectors.toList());
        boolean res = roomInnerService.batchBanned(vo.getRoomId(), accountIds, vo.getStatus(), thirdId);
        if (!res) {
            throw new BusinessException(BizErrorCode.BIZ_SWITCH_MUTE_FAILED);
        }
        return roomInnerService.getBannedNum(vo.getRoomId());
    }

    @Override
    public Boolean setKicked(SetKickedReqVO vo, VssToken token) {
        RoomsEntityDTO room = roomInnerService.getRoomInfo(vo.getRoomId());
        if (room == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        List<String> accountIdList = Splitter.on(",").splitToList(vo.getReceiveAccountId());
        List<Integer> accountIds = accountIdList.stream().map(Integer::parseInt).collect(Collectors.toList());
        Integer thirdId = token.getThirdPartyUserId();
        boolean res = roomInnerService.batchKick(vo.getRoomId(), accountIds, vo.getStatus(), thirdId);
        if (!res) {
            throw new BusinessException(BizErrorCode.BIZ_SWITCH_KICK_FAILED);
        }
        return res;
    }

    @Override
    public Boolean setDeviceStatus(SetDeviceStatusReqVO reqVO, VssToken token, RoomsEntityDTO room) {
        RoomJoinsEntityDTO joinUser = roomInnerService.getRoomJoins(token.getThirdPartyUserId(), reqVO.getRoomId());
        RoomJoinsEntityDTO reciveJoinUser = roomInnerService.getRoomJoins(reqVO.getReceiveAccountId(), reqVO.getRoomId());
        if (joinUser.getRoleName().equals(RoomConstant.HOST) || joinUser.getJoinId().toString()
                .equals(reciveJoinUser.getJoinId()
                        .toString())) {
            setSpeakerAttr(reciveJoinUser, reqVO.getDevice(), reqVO.getStatus(), room.getChannelId());
        }
        return true;
    }

    @Override
    public Boolean setMainScreen(SetMainScreenReqVO reqVO, VssToken token) {
        String roomId = reqVO.getRoomId();
        Integer receiverId = reqVO.getReceiveAccountId();
        Integer thirdId = token.getThirdPartyUserId();
        RoomJoinsEntityDTO host = roomInnerService.getCurrentJoinUserByThirdId(roomId, thirdId);
        if (!host.getRoleName().equals(RoomConstant.HOST)) {
            throw new BusinessException(BizErrorCode.BIZ_NO_PERMISSION);
        }
        RoomsEntityDTO room = roomInnerService.getRoomInfo(roomId);
        if (null == room) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        if (!Objects.equals(room.getStatus(), RoomStatusEnum.LIVE_BROADCAST.getStatus())
                && !Objects.equals(room.getStatus(), RoomStatusEnum.END_BROADCAST.getStatus())) {
            throw new BusinessException(BizErrorCode.BIZ_NOT_START);
        }

        // 检查参会用户信息
        RoomJoinsEntityDTO joiner = roomInnerService.getRoomJoinInfo(receiverId, roomId);
        if (null == joiner) {
            return false;
        }

        redisTemplate.opsForHash().put(RedisKey.INTERACT_GLOBAL + roomId, RoomConstant.MAIN_SCREEN, receiverId);

        // 向频道发送消息
        Map<String, Object> message = new HashMap<>(16);
        message.put("type", "vrtc_big_screen_set");
        message.put(InavConstant.ROOM_JOIN_ID, joiner.getAccountId());
        message.put(InavConstant.NICK_NAME, joiner.getNickname());
        message.put(InavConstant.ROOM_ROLE, Integer.valueOf(joiner.getRoleName()));
        message.put(InavConstant.PROHIBIT_SPEAK, joiner.getIsBanned());
        message.put(InavConstant.KICKED_OUT, joiner.getIsKicked());
        message.put(InavConstant.DEVICE_TYPE, joiner.getDeviceType());
        message.put(InavConstant.DEVICE_STATUS, joiner.getDeviceStatus());
        message.put(InavConstant.AVATAR, joiner.getAvatar());
        message.put(InavConstant.TARGET_ID, joiner.getAccountId());
        paasService.sendMessage(room.getChannelId(), JSON.toJSONString(message));
        return true;
    }

    @Override
    public void speak(SpeakReqVO reqVO, VssToken token) {
        // 参会用户
        String roomId = reqVO.getRoomId();
        Integer accountId = token.getThirdPartyUserId();
        //获取参会人员信息
        RoomJoinsEntityDTO joinUser = roomInnerService.getRoomJoinInfo(accountId, roomId);
        if (null == joinUser) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        // 4、上麦操作
        addSpeaker(joinUser, 1);
        // 5、上麦时间记录
        Map<String, String> param = new HashMap<>(4);
        param.put("room_id", roomId);
        param.put(InavConstant.ACCOUNT_ID, String.valueOf(accountId));
        // 6、移除上麦中
        wheatCacheService.removeCachedWaitingForWheat(roomId, accountId);
    }

    @Override
    public Boolean noSpeak(NoSpeakReqVO reqVO, VssToken token) {
        // 2、根据room_id和用户id查看当前房间成员中是否包含该用户。
        // 参会用户
        Integer accountId = token.getThirdPartyUserId();
        RoomJoinsEntityDTO joinUser = roomInnerService.getRoomJoinInfo(accountId, reqVO.getRoomId());
        if (null == joinUser) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        // 被操作用户 receiveJoinUser
        RoomJoinsEntityDTO receiveJoinUser = roomInnerService.getRoomJoinInfo(reqVO.getReceiveAccountId(), reqVO.getRoomId());
        if (null == receiveJoinUser) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        // 3、执行下麦操作。
        if (RoomConstant.HOST.equals(joinUser.getRoleName()) || String.valueOf(receiveJoinUser.getJoinId())
                .equals(String.valueOf(joinUser.getJoinId()))) {
            redisTemplate.opsForHash().delete(RedisKey.INTERACT_SPEAKER + receiveJoinUser.getRoomId(),
                    String.valueOf(receiveJoinUser.getAccountId()));
            Object mainScreenAccountId = redisTemplate.opsForHash().get(RedisKey.INTERACT_GLOBAL + receiveJoinUser.getRoomId(),
                    RoomConstant.MAIN_SCREEN);
            RoomsEntityDTO room = roomInnerService.getRoomInfo(joinUser.getRoomId());
            // 主画面
            if ((receiveJoinUser.getAccountId().toString().equals(String.valueOf(mainScreenAccountId)))) {
                // 根据room_id和role_name查询join数据。
                QueryRoomJoinsBySelectOneReqVO queryJoinsReqVO = new QueryRoomJoinsBySelectOneReqVO();
                queryJoinsReqVO.setRoomId(receiveJoinUser.getRoomId());
                queryJoinsReqVO.setRoleName(RoomConstant.HOST);
                RoomJoinsEntityDTO webinarHost = roomInnerService.queryRoomJoinsBySelectOne(queryJoinsReqVO);
                if (null != webinarHost) {
                    setDocPermission(room, webinarHost);
                    setMainScreen(room, webinarHost);
                }

            }
            // 发送消息
            JSONObject object = new JSONObject();
            object.put("type", RoomConstant.VRTC_DISCONNECT_SUCCESS);
            object.put(InavConstant.ROOM_JOIN_ID, joinUser.getAccountId());
            object.put(InavConstant.TARGET_ID, receiveJoinUser.getAccountId());
            object.put("vrtc_reason", reqVO.getReason());
            object.put(InavConstant.NICK_NAME, receiveJoinUser.getNickname());
            object.put(InavConstant.ROOM_ROLE, receiveJoinUser.getRoleName());
            String body = JsonUtil.toJsonString(object);
            paasService.sendMessage(room.getChannelId(), body, null, null, null, null);
        } else {
            throw new BusinessException(BizErrorCode.BIZ_NO_PERMISSION);
        }
        // 4、下麦上报--大数据上报。
        JSONObject object = new JSONObject();
        object.put("room_id", reqVO.getRoomId());
        object.put("account_id", joinUser.getAccountId());
        object.put("role_name", joinUser.getRoleName());
        object.put("vss_token", reqVO.getVssToken());

        return true;
    }

    /**
     * 设置上麦人, 文档演示权限
     *
     * @param token vss token
     * @param reqVO req param
     */
    @Override
    public Boolean setDocPermissionAction(SetDocPermissionReqVO reqVO, VssToken token) {
        // 2、根据room_id和用户id查看当前房间成员中是否包含该用户。
        Integer thridId = token.getThirdPartyUserId();
        RoomJoinsEntityDTO joinUser = roomInnerService.getRoomJoins(thridId, reqVO.getRoomId());
        if (null == joinUser) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        RoomJoinsEntityDTO receiveJoinUser = roomInnerService.getRoomJoins(reqVO.getReceiveAccountId(), reqVO.getRoomId());
        if (null == receiveJoinUser) {
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        if (!RoomConstant.HOST.equals(joinUser.getRoleName())) {
            throw new BusinessException(BizErrorCode.BIZ_NO_PERMISSION);
        }
        RoomsEntityDTO room = roomInnerService.getRoomInfo(joinUser.getRoomId());
        //设置文档操作权限
        setDocPermission(room, receiveJoinUser);
        return true;
    }

    /**
     * 设置文档操作权限
     *
     * @param room     房间
     * @param joinUser 操作人
     */
    private void setDocPermission(RoomsEntityDTO room, RoomJoinsEntityDTO joinUser) {
        redisTemplate.opsForHash()
                .put(RedisKey.INTERACT_GLOBAL + joinUser.getRoomId(), RoomConstant.DOC_PERMISSION, joinUser.getAccountId());
        JSONObject object = new JSONObject();
        object.put("type", RoomConstant.VRTC_SPEAKER_SWITCH);
        object.put(InavConstant.ROOM_JOIN_ID, joinUser.getAccountId());
        object.put(InavConstant.NICK_NAME, joinUser.getNickname());
        object.put(InavConstant.ROOM_ROLE, joinUser.getRoleName());
        object.put(InavConstant.PROHIBIT_SPEAK, joinUser.getIsBanned());
        object.put(InavConstant.KICKED_OUT, joinUser.getIsKicked());
        object.put(InavConstant.DEVICE_TYPE, joinUser.getDeviceType());
        object.put(InavConstant.DEVICE_STATUS, joinUser.getDeviceStatus());
        object.put(InavConstant.AVATAR, joinUser.getAvatar());
        object.put(InavConstant.TARGET_ID, joinUser.getAccountId());
        String body = JsonUtil.toJsonString(object);
        paasService.sendMessage(room.getChannelId(), body, null, null, null, null);

    }

    private void setMainScreen(RoomsEntityDTO room, RoomJoinsEntityDTO joinUser) {
        redisTemplate.opsForHash()
                .put(RedisKey.INTERACT_GLOBAL + joinUser.getRoomId(), RoomConstant.MAIN_SCREEN, joinUser.getAccountId());
        JSONObject object = new JSONObject();
        object.put("type", RoomConstant.VRTC_BIG_SCREEN);
        //参会id
        object.put(InavConstant.ROOM_JOIN_ID, joinUser.getAccountId());
        //昵称
        object.put(InavConstant.NICK_NAME, joinUser.getNickname());
        //用户角色（1:老师 2:学员 3:助教 4:嘉宾 5:监课）
        object.put(InavConstant.ROOM_ROLE, joinUser.getRoleName());
        //是否禁言Y是N否
        object.put(InavConstant.PROHIBIT_SPEAK, joinUser.getIsBanned());
        //是否踢出Y是N否
        object.put(InavConstant.KICKED_OUT, joinUser.getIsKicked());
        //设备类型0其他1手机端2PC
        object.put(InavConstant.DEVICE_TYPE, joinUser.getDeviceType());
        //设备状态0未检测1可上麦2不可上麦
        object.put(InavConstant.DEVICE_STATUS, joinUser.getDeviceStatus());
        //头像
        object.put(InavConstant.AVATAR, joinUser.getAvatar());
        //参会id
        object.put(InavConstant.TARGET_ID, joinUser.getAccountId());
        String body = JsonUtil.toJsonString(object);
        paasService.sendMessage(room.getChannelId(), body, null, null, null, null);
    }

    /**
     * @param joinUser 参会用户
     * @param type     上麦方式 1举手 2邀请
     */
    private void addSpeaker(RoomJoinsEntityDTO joinUser, Integer type) {
        if (!RoomConstant.HOST.equals(joinUser.getRoleName())) {
            String key = RedisKey.INTERACT_HANDSUP + joinUser.getAccountId();
            Object value = redisTemplate.opsForValue().get(key);
            //获取邀请列表
            Map<String, Long> inviteList = getInviteList(joinUser.getRoomId());
            if (!"2".equals(String.valueOf(value)) && !inviteList.containsKey(String.valueOf(joinUser.getAccountId()))) {
                throw new BusinessException(BizErrorCode.BIZ_NO_PERMISSION);
            }
        }
        boolean flag = redisTemplate.opsForHash().hasKey(RedisKey.INTERACT_SPEAKER + joinUser.getRoomId(),
                String.valueOf(joinUser.getAccountId()));
        if (!flag) {
            //获取上麦用户数量
            Integer speakerCount = getSpeakerCount(joinUser);
            if (speakerCount <= RoomConstant.SPEAKER_MAX_NUM) {
                JSONObject object = new JSONObject();
                object.put(InavConstant.NICK_NAME, joinUser.getNickname());
                object.put("role_name", joinUser.getRoleName());
                object.put("account_id", joinUser.getAccountId());
                object.put("audio", 1);
                object.put("video", 1);
                String body = JsonUtil.toJsonString(object);
                redisTemplate.opsForHash()
                        .put(RedisKey.INTERACT_SPEAKER + joinUser.getRoomId(), String.valueOf(joinUser.getAccountId()), body);
                //上麦删除邀请
                redisTemplate.opsForZSet()
                        .remove(RedisKey.INTERACT_INVITE + joinUser.getRoomId(), String.valueOf(joinUser.getAccountId()));
                RoomsEntityDTO room = roomInnerService.getRoomInfo(joinUser.getRoomId());
                JSONObject object1 = new JSONObject();
                //上麦
                object1.put("type", RoomConstant.VRTC_CONNECT_SUCCESS);
                //参会id
                object1.put(InavConstant.ROOM_JOIN_ID, joinUser.getAccountId());
                //昵称
                object1.put(InavConstant.NICK_NAME, joinUser.getNickname());
                //用户角色（1:老师 2:学员 3:助教 4:嘉宾 5:监课）
                object1.put(InavConstant.ROOM_ROLE, joinUser.getRoleName());
                //是否禁言Y是N否
                object1.put(InavConstant.PROHIBIT_SPEAK, joinUser.getIsBanned());
                //是否踢出Y是N否
                object1.put(InavConstant.KICKED_OUT, joinUser.getIsKicked());
                //设备类型0其他1手机端2PC
                object1.put(InavConstant.DEVICE_TYPE, joinUser.getDeviceType());
                //设备状态0未检测1可上麦2不可上麦
                object1.put(InavConstant.DEVICE_STATUS, joinUser.getDeviceStatus());
                //头像
                object1.put(InavConstant.AVATAR, joinUser.getAvatar());
                //麦克风开关on开off关
                object1.put("vrtc_audio_status", "on");
                //摄像头开关on开off关
                object1.put("vrtc_video_status", "on");
                //连麦类型apply主动申请上麦invite主持人邀请上麦
                object1.put("vrtc_connect_type", type);
                String body1 = JsonUtil.toJsonString(object1);
                paasService.sendMessage(room.getChannelId(), body1, null, null, null, null);
            } else {
                throw new BusinessException(BizErrorCode.BIZ_SPEAKER_FULL);
            }
        } else {
            throw new BusinessException(BizErrorCode.BIZ_REPEAT_SPEAKER);
        }
    }

    /**
     * 设置 连麦属性
     *
     * @param receiveJoinUser receiver
     * @param status          0 取消静音  1静音
     * @param device          device type,1=麦克风,2=摄像头
     */
    private void setSpeakerAttr(RoomJoinsEntityDTO receiveJoinUser, String device, String status, String channelId) {
        String roomId = receiveJoinUser.getRoomId();
        String accountId = receiveJoinUser.getAccountId().toString();
        Object speaker = redisTemplate.opsForHash().get(RedisKey.INTERACT_SPEAKER + roomId, accountId);
        if (speaker != null) {
            JSONObject obj = JSON.parseObject(speaker.toString());
            //代表麦克风
            if (InavConstant.DEVICE_TYPE_MICROPHONE.equals(device)) {
                obj.put("audio", status);
                String mbody = createMbody(status, accountId);
                paasService.sendMessage(channelId, mbody);
            }
            //代表摄像头
            if (InavConstant.DEVICE_TYPE_CAMERA.equals(device)) {
                obj.put("video", status);
                String sbody = createsMbody(status, accountId);
                paasService.sendMessage(channelId, sbody);
            }
            redisTemplate.opsForHash().put(RedisKey.INTERACT_SPEAKER + roomId, accountId, obj.toJSONString());
        } else {
            log.error("redis没有查询到数据");
        }

    }

    /**
     * 构造摄像头
     *
     * @param status    camera status
     * @param accountId account id
     * @return message json
     */
    private String createsMbody(String status, String accountId) {
        JSONObject json = new JSONObject();
        json.put("type", "vrtc_frames_forbid");
        if ("1".equals(status)) {
            json.put("type", "vrtc_frames_display");
        }
        json.put("room_join_id", accountId);
        json.put("target_id", accountId);

        return json.toJSONString();
    }

    /**
     * 构造  麦克风 body消息体
     *
     * @param status    microphone status
     * @param accountId account id
     * @return message json
     */
    private String createMbody(String status, String accountId) {
        JSONObject json = new JSONObject();
        json.put("type", "vrtc_mute");
        if ("1".equals(status)) {
            json.put("type", "vrtc_mute_cancel");
        }
        json.put(InavConstant.ROOM_JOIN_ID, accountId);
        json.put("target_id", accountId);

        return json.toJSONString();

    }

    private RoomsEntityDTO checkRoomLive(String roomId) {
        RoomsEntityDTO roomInfo = roomInnerService.getRoomInfo(roomId);
        if (roomInfo == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        if (roomInfo.getStatus() != 1) {
            throw new BusinessException(BizErrorCode.BIZ_NOT_START);
        }
        return roomInfo;
    }

    @Override
    public void preBreakSpeak(PreBreakSpeakReqVO reqVO, Integer accountId) {
        String roomId = reqVO.getRoomId();
        String speakerAccountId = reqVO.getReceiveAccountId();
        // 根据房间id及被操作者用户id获取房间参与信息
        RoomJoinsEntityDTO joinInfo = checkRoomJoinInfo(roomId, Integer.parseInt(speakerAccountId));
        // 获取房间频道信息
        String channelId = getRoomChannelId(roomId);

        // 操作者用户id
        String operatorAccountId = String.valueOf(accountId);
        // 构建广播消息体
        MicBroadcastMessageDTO broadcastMessage = new MicBroadcastMessageDTO.Builder(RoomConstant.VRTC_DISCONNECTING_SPEAK)
                .setOperatorInfo(joinInfo.getNickname(), operatorAccountId, joinInfo.getRoleName(), joinInfo.getDeviceType())
                .setTargetInfo(speakerAccountId).build();
        // 发送广播消息
        paasService.sendMessage(channelId, JSON.toJSONString(broadcastMessage));
    }

    @SuppressWarnings("unchecked")
    @Override
    public void unableSpeak(UnableSpeakReqVO reqVO, Integer accountId) {
        String roomId = reqVO.getRoomId();
        // 根据房间id及用户id获取房间参与信息
        RoomJoinsEntityDTO joinInfo = checkRoomJoinInfo(roomId, accountId);
        // 获取房间频道信息
        String channelId = getRoomChannelId(roomId);

        String joinerId = String.valueOf(accountId);
        // 按人员清理邀请缓存数据
        redisTemplate.opsForZSet().remove(RedisKey.INTERACT_INVITE + roomId, joinerId);
        // 将用户下麦（清理对应用户的上麦缓存数据）
        redisTemplate.opsForHash().delete(RedisKey.INTERACT_SPEAKER + roomId, joinerId);
        // 移除上麦中
        wheatCacheService.removeCachedWaitingForWheat(roomId, accountId);

        // 构建广播消息体
        MicBroadcastMessageDTO broadcastMessage = new MicBroadcastMessageDTO.Builder(RoomConstant.VRTC_CONNECT_UNABLE_SPEAK)
                .setOperatorInfo(joinInfo.getNickname(), joinerId, joinInfo.getRoleName(), joinInfo.getDeviceType())
                .setTargetInfo(joinerId).build();
        // 发送广播消息
        paasService.sendMessage(channelId, JSON.toJSONString(broadcastMessage));
    }

    @Override
    public Boolean queueAdd(Integer accountId, Integer ilId) {
        try {
            String key = RedisKey.QUEUE_PERF_CTL_ILID + ilId;

            //防止 list里面重复
            if (redisTemplate.opsForSet().isMember(RedisKey.ALL_ACCOUNTIDS + ilId, accountId)) {
                return true;
            }

            redisTemplate.opsForList().rightPush(key, accountId);

            redisTemplate.opsForSet().add(RedisKey.ALL_ACCOUNTIDS + ilId, accountId);

            redisTemplate.expire(RedisKey.ALL_ACCOUNTIDS + ilId, 3600 * 24, TimeUnit.SECONDS);

            redisTemplate.opsForSet().add(RedisKey.QUEUE_CTRL, key);

        } catch (Exception ex) {
            log.error("并发限制入队列失败:", ex);
        }
        return true;
    }

    /**
     * 获取是否弹出引导关注/订阅信息
     * @param requestVO
     * @return
     */
    @Override
    public UserGuideInfoResponseVO getUserGuideInfo(UserGuideInfoRequestVO requestVO) {
        UserGuideInfoResponseVO responseVO = new UserGuideInfoResponseVO();
        Integer ilId = requestVO.getIlId();
        Token token = TokenThreadLocal.getToken();
        Integer accountId = token.getAccountId();
        String accountIdStr = String.valueOf(accountId);
        String watchUserCode = token.getThirdPartyUserId();
        String key = StrUtil.indexedFormat("{0}:{1}:{2}",RedisKey.ROOM_LIVE_DATE_SUBSCRIBE_ALERT, LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE),String.valueOf(ilId));
//        判定这房间今天是否有缓存弹出记录,没有则这个房间需要进行验证
//        判定这人是否在这个房间下弹出过,使用set进行缓存
        if (StrUtil.isNotBlank(watchUserCode) && (!redisTemplate.hasKey(key) || !redisTemplate.opsForSet().isMember(key,accountIdStr))) {
            responseVO = setUserGuideInfo(ilId, accountId, watchUserCode);
        }else{
            responseVO.setShowStatus(UserGuideShowStatusEnum.NOT_SHOW.getValue());
            responseVO.setShowType(UserGuideShowTypeEnum.SHOW_IP.getValue());
        }
        return responseVO;
    }

    /**
     * 设置观众是否需要弹出引导
     * @param ilId 房间id
     * @param accountId 观众id
     * @param watchUserCode 观众usercode
     */
    private UserGuideInfoResponseVO setUserGuideInfo(Integer ilId, Integer accountId, String watchUserCode) {
        UserGuideInfoResponseVO responseVO = new UserGuideInfoResponseVO();
        Integer showStatus = UserGuideShowStatusEnum.NOT_SHOW.getValue();
        Integer showType = UserGuideShowTypeEnum.SHOW_IP.getValue();
        String accountIdStr = String.valueOf(accountId);
        RoomsEntityDTO roomInfoById = Optional.ofNullable(roomInnerService.getRoomInfoById(ilId)).orElseThrow(() -> new BusinessException(BizErrorCode.EMPTY_ROOM));
        List<ThirdUserVO> anchorInfoList = roomInnerService.getCacheRoomAnchorInfoList(ilId);
        Integer columnId = roomInfoById.getColumnId();
        if (CollUtil.isNotEmpty(anchorInfoList)) {
            ThirdUserVO thirdUserVO = anchorInfoList.get(0);
            String anchorUserCode = thirdUserVO.getUserCode();
            String zoneId = thirdUserVO.getZoneId();
            if (StrUtil.isNotBlank(zoneId)) {
                String zoneKey = StrUtil.indexedFormat("{0}:{1}:{2}", RedisKey.ROOM_TGH_DATE_SUBSCRIBE_ALERT, LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE), zoneId);
//            判定这 投顾 IP 今天是否有缓存弹出记录,没有则这个 投顾 IP 需要进行验证
//            判定这人是否在这个 投顾 IP 下弹出过,使用set进行缓存
                Boolean zoneKeyExists = redisTemplate.hasKey(zoneKey);
                Boolean accountInZoneKeyExists = redisTemplate.opsForSet().isMember(zoneKey, accountIdStr);
                if (!zoneKeyExists || !accountInZoneKeyExists) {
                    String userFollowGuideKey = StrUtil.indexedFormat("{0}:{1}:{2}", RedisKey.GUO_SEN_STAFF_USER_FOLLOW_DATE, zoneId, LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE));
                    Boolean checkUserFollowTgh = false;
                    if (!redisTemplate.hasKey(userFollowGuideKey) && !redisTemplate.opsForHash().hasKey(userFollowGuideKey, accountIdStr)) {
                        checkUserFollowTgh = checkUserFollowTgh(anchorUserCode, watchUserCode);
                        redisTemplate.opsForHash().put(userFollowGuideKey,accountIdStr,checkUserFollowTgh);
                        setGuideCacheExpire(userFollowGuideKey);
                    }else{
                        checkUserFollowTgh = (Boolean) redisTemplate.opsForHash().get(userFollowGuideKey,accountIdStr);
                        if (Objects.isNull(checkUserFollowTgh)) {
                            checkUserFollowTgh = checkUserFollowTgh(anchorUserCode, watchUserCode);
                            redisTemplate.opsForHash().put(userFollowGuideKey,accountIdStr,checkUserFollowTgh);
                            setGuideCacheExpire(userFollowGuideKey);
                        }
                    }
                    if (!checkUserFollowTgh) {
                        showStatus = UserGuideShowStatusEnum.SHOW.getValue();
                        showType = UserGuideShowTypeEnum.SHOW_IP.getValue();
                        responseVO.setShowStatus(showStatus);
                        responseVO.setShowType(showType);
                        return responseVO;
                    }
                }
            }
        }
        if (Objects.equals(showStatus, UserGuideShowStatusEnum.NOT_SHOW.getValue()) && !Objects.equals(columnId, 0)) {
            String columnKey = StrUtil.indexedFormat("{0}:{1}:{2}", RedisKey.ROOM_COLUMN_DATE_SUBSCRIBE_ALERT, LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE), columnId);
            if (!redisTemplate.hasKey(columnKey) || !redisTemplate.opsForSet().isMember(columnKey, accountIdStr)) {
                if (!liveColumnService.isSubscribe(columnId, accountId)) {
                    showStatus = UserGuideShowStatusEnum.SHOW.getValue();
                    showType = UserGuideShowTypeEnum.SHOW_COLUMN.getValue();
                    responseVO.setShowStatus(showStatus);
                    responseVO.setShowType(showType);
                    return responseVO;
                }
            }
        }
        responseVO.setShowStatus(showStatus);
        responseVO.setShowType(showType);
        return responseVO;
    }

    /**
     * 校验观众是否关注过该主播
     * @param anchorUserCode 主播用户 usercode
     * @param watchUserCode 观众用户 usercode
     * @return
     */
    private Boolean checkUserFollowTgh(String anchorUserCode,String watchUserCode) {
        GetTghInfoDTO dto = new GetTghInfoDTO();
        dto.setNotesid(anchorUserCode);
        dto.setUserCode(watchUserCode);
        GuoxinInvestmentAdvisorTeamDTO advisorTeamDTO = guoXinService.getGuoxinInvestmentAdvisorTeamDTO(dto);
        String isFollow = advisorTeamDTO.getIsFollow();
        return StrUtil.equals(FollowEnum.IS_FOLLOW.getValue(),isFollow);
    }


    /**
     * 关注国信的投顾 IP
     * @param vo
     * @return
     */
    @Override
    public Boolean followGuoxinZone(FollowGuoxinZoneRequestVO vo) {
        String zoneId = vo.getZoneId();
        Token token = TokenThreadLocal.getToken();
        String userCode = token.getThirdPartyUserId();
        if (StrUtil.isNotBlank(userCode)) {
            GuoxinFollowTghDTO dto = new GuoxinFollowTghDTO();
            dto.setZoneId(zoneId);
            dto.setOpType("1");
            dto.setUserCode(userCode);
            guoXinService.followTgh(dto);
        }else{
            throw new BusinessException(BizErrorCode.BIZ_INVALID_USER);
        }
        return true;
    }

    /**
     *  上报已经弹出了预告
     * @param vo
     * @return
     */
    @Override
    public Boolean reportGuideShown(ReportGuideShownRequestVO vo) {
        Integer ilId = vo.getIlId();
        Token token = TokenThreadLocal.getToken();
        Integer accountId = token.getAccountId();
        String accountIdStr = String.valueOf(accountId);
        String roomKey = StrUtil.indexedFormat("{0}:{1}:{2}",RedisKey.ROOM_LIVE_DATE_SUBSCRIBE_ALERT, LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE),String.valueOf(ilId));
        RoomsEntityDTO roomInfoById = Optional.ofNullable(roomInnerService.getRoomInfoById(ilId)).orElseThrow(() -> new BusinessException(BizErrorCode.EMPTY_ROOM));

        Integer columnId = roomInfoById.getColumnId();
        UserGuideInfoRequestVO tempVO = new UserGuideInfoRequestVO();
        tempVO.setIlId(ilId);

        UserGuideInfoResponseVO userGuideInfo = getUserGuideInfo(tempVO);
//        上报房间的用户弹出预告
        redisTemplate.opsForSet().add(roomKey, accountIdStr);
        setGuideCacheExpire(roomKey);
        if (Objects.equals(userGuideInfo.getShowType(), UserGuideShowTypeEnum.SHOW_IP.getValue())) {
            List<ThirdUserVO> anchorInfoList = roomInnerService.getCacheRoomAnchorInfoList(ilId);
            if (CollUtil.isNotEmpty(anchorInfoList)) {
                ThirdUserVO thirdUserVO = anchorInfoList.get(0);
                String zoneId = thirdUserVO.getZoneId();
                if (StrUtil.isNotBlank(zoneId)) {
                    String zoneKey = StrUtil.indexedFormat("{0}:{1}:{2}", RedisKey.ROOM_TGH_DATE_SUBSCRIBE_ALERT, LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE), zoneId);
                    redisTemplate.opsForSet().add(zoneKey, accountIdStr);
                    setGuideCacheExpire(zoneKey);
                }
            }
        }else{
            if (!Objects.equals(columnId,0)) {
                String columnKey = StrUtil.indexedFormat("{0}:{1}:{2}", RedisKey.ROOM_COLUMN_DATE_SUBSCRIBE_ALERT, LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE), columnId);
                redisTemplate.opsForSet().add(columnKey, accountIdStr);
                setGuideCacheExpire(columnKey);
            }
        }
        return true;
    }

    /**
     * 设置弹出提醒缓存的过期时间
     * @param cacheKey 设置的 key
     */
    private void setGuideCacheExpire(String cacheKey) {
        redisTemplate.expire(cacheKey, 1, TimeUnit.DAYS);
    }
}
